package 遍历器;

import 作用域相关.*;
import 抽象语法树节点.*;

public class 引用监听器 extends 抽象语法树基础监听器 {

    private final 符号表 符号表;

    public 引用监听器(符号表 符号表) {
        this.符号表 = 符号表;
    }

    @Override
    public void 进入类定义(类定义 节点) {
        var 类符号 = (类符号) 节点.符号;

        if (节点.父类名 != null) {
            System.out.println("引用父类: " + 节点.父类名);
            类符号.父类 = (类符号) 类符号.解析(节点.父类名);
        }
    }

    @Override
    public void 进入字段定义(字段定义 节点) {
        var 字段符号 = (字段符号) 节点.符号;

        System.out.println("类字段 " + 节点.名称 + " 引用类型: " + 节点.类型);
        字段符号.类型 = (类型) 节点.作用域.解析(节点.类型);
    }

    @Override
    public void 进入方法定义(方法定义 节点) {
        var 方法符号 = (方法符号) 节点.符号;

        if (节点.返回值类型 != null) {
            System.out.println("方法 " + 节点.名称 + " 引用返回值类型: " + 节点.返回值类型);
            方法符号.类型 = (类型) 节点.作用域.解析(节点.返回值类型);
        }
    }

    @Override
    public void 进入参数定义(参数定义 节点) {
        var 变量符号 = (变量符号) 节点.符号;

        System.out.println("方法参数 " + 节点.名称 + " 引用类型: " + 节点.类型);
        变量符号.类型 = (类型) 节点.作用域.解析(节点.类型);
    }

    @Override
    public void 进入变量定义(变量定义 节点) {
        var 变量符号 = (变量符号) 节点.符号;

        System.out.println("变量 " + 节点.名称 + " 引用类型: " + 节点.类型);
        变量符号.类型 = (类型) 节点.作用域.解析(节点.类型);
    }

    @Override
    public void 离开函数调用(函数调用 节点) {

        var 函数名 = ((标识符) 节点.对象).名称;
        var 符号 = 节点.作用域.解析(函数名);
        if (!(符号 instanceof 方法符号)) {
            System.err.println("行 " + 节点.idToken.getLine() + ": 错误: 找不到方法符号 " + 函数名);
        } else {
            System.out.println("行 " + 节点.idToken.getLine() + " 解析函数 " + 函数名 + " 为: " + 符号);
        }
    }


    @Override
    public void 离开成员访问(成员访问 节点) {

        符号 成员符号 = null; // 右子树的符号

        if (节点.对象 instanceof This访问器) {
            var 类 = 作用域相关.符号表.取外围类(节点.作用域);
            System.out.println("行 " + 节点.idToken.getLine() + " 解析 this 为 " + 类);
            成员符号 = 类.解析成员(节点.成员名);
        } else if (节点.对象 instanceof Super访问器) {
            var 类 = 作用域相关.符号表.取外围类(节点.作用域);
            System.out.println("行 " + 节点.idToken.getLine() + " 解析 super 为 " + 类);

            成员符号 = 类.父类.解析成员(节点.成员名);
        } else if (节点.对象 instanceof 标识符 标识符) {
            var 名称符号 = 节点.作用域.解析(标识符.名称);

            类符号 类;
            if (名称符号 instanceof 字段符号 字段符号) {
                类 = (类符号) 字段符号.类型;
            } else if (名称符号 instanceof 变量符号 变量符号) {
                类 = (类符号) 变量符号.类型;
            } else {
                System.err.println("行 " + 节点.idToken.getLine() + " 错误: 找不到字段或变量符号 " + 标识符.名称);
                return;
            }

            System.out.println("行 " + 节点.idToken.getLine() + " 解析ID " + 标识符.名称 + " 为 " + 类);

            成员符号 = 类.解析成员(节点.成员名);
        } else if (节点.对象 instanceof 整型字面量) {
            // 什么都不用处理
        } else {
            var 类 = (类符号) 节点.对象.作用域;
            成员符号 = 类.解析成员(节点.成员名);
        }

        if (成员符号 instanceof 字段符号) {
            System.out.println("行 " + 节点.idToken.getLine() + " 解析成员变量 ." + 节点.成员名 + " 为 " + 成员符号);
            节点.符号 = 成员符号;
            节点.作用域 = 成员符号.所在作用域;
        } else {
            System.err.println("行 " + 节点.idToken.getLine() + " 错误: 找不到字段符号 " + 节点.成员名);
        }
    }

    @Override
    public void 进入标识符(标识符 节点) { // 注意这个方法不能省掉，这里还得用于去处理直接的变量访问
        var 符号 = 作用域相关.符号表.解析ID(节点);
        if (符号 == null) {
            System.err.println("行 " + 节点.idToken.getLine() + ": 错误: 找不到符号 " + 节点.名称);
        }
    }

    @Override
    public void 离开方法调用(方法调用 节点) {

        符号 符号 = null; // 右子树的符号

        if (节点.对象 instanceof This访问器) {
            var 类 = 作用域相关.符号表.取外围类(节点.作用域);

            if (类.取成员().containsKey(节点.方法名)) {
                符号 = 类.取成员().get(节点.方法名);
            }
        } else if (节点.对象 instanceof Super访问器) {
            var 类 = 作用域相关.符号表.取外围类(节点.作用域).父类;
            符号 = 类.父类.解析成员(节点.方法名);

        } else if (节点.对象 instanceof 标识符 标识符) {
            var 名称符号 = 节点.作用域.解析(标识符.名称);

            类符号 类;
            if (名称符号 instanceof 字段符号 字段符号) {
                类 = (类符号) 字段符号.类型;
            } else if (名称符号 instanceof 变量符号 变量符号) {
                类 = (类符号) 变量符号.类型;
            } else {
                System.err.println("行 " + 节点.idToken.getLine() + " 错误: 找不到字段或变量符号 " + 标识符.名称);
                return;
            }

            符号 = 类.解析成员(节点.方法名);
        } else if (节点.对象 instanceof 整型字面量) {
            // 什么都不用处理
        } else {
            var 类 = (类符号) 节点.对象.作用域;
            符号 = 类.解析成员(节点.方法名);
        }

        if (符号 instanceof 方法符号 方法符号) {
            System.out.println("行 " + 节点.idToken.getLine() + " 解析方法 ." + 节点.方法名 + " 为 " + 符号);
            节点.符号 = 方法符号; // todo: 暂时不支持方法返回一个类
            节点.作用域 = 符号.所在作用域;
        } else {
            System.err.println("行 " + 节点.idToken.getLine() + " 错误: 找不到方法 " + 节点.方法名);
        }
    }
}
